use godot::{classes::{mesh::{ArrayType, PrimitiveType}, ArrayMesh}, prelude::*};

use crate::chunk::Chunk;

#[derive(GodotClass)]
#[class(init, base=ArrayMesh)]
pub struct ChunkMesh {
    base: Base<ArrayMesh>,
    chunk: Chunk,
}

#[godot_api]
impl ChunkMesh {
    #[func]
    pub fn set_voxel(&mut self, pos: Vector3i, color: Color) {
        self.chunk.set_voxel(pos, color);
    }

    #[func]
    pub fn remove_voxel(&mut self, pos: Vector3i) {
        self.chunk.remove_voxel(pos);
    }

    #[func]
    pub fn rebake(&mut self) {
        self.base_mut().clear_surfaces();
        
        let (quads, normals, colors) = self.chunk.greedy_meshing();
        
        let vertices: PackedVector3Array = quads
            .iter()
            .flat_map(|v| v)
            .map(|x| x.cast_float())
            .collect();

        let indices: PackedInt32Array = (0..quads.len())
            .flat_map(|idx| {
                let begin = idx as i32 * 4;
                [begin, begin + 1, begin + 2, begin, begin + 2, begin + 3]
            })
            .collect();

        let normals: PackedVector3Array = normals
            .iter()
            .flat_map(|x| core::iter::repeat_n(x.cast_float(), 4))
            .collect();

        let colors: PackedColorArray = colors
            .iter()
            .flat_map(|x| core::iter::repeat_n(x.clone(), 4))
            .collect();

        let mut arrays = Array::new();
        arrays.resize(ArrayType::MAX.ord() as _, &Variant::nil());

        arrays.set(ArrayType::VERTEX.ord() as _, &vertices.to_variant());
        arrays.set(ArrayType::INDEX.ord() as _, &indices.to_variant());
        arrays.set(ArrayType::NORMAL.ord() as _, &normals.to_variant());
        arrays.set(ArrayType::COLOR.ord() as _, &colors.to_variant());

        self.base_mut().add_surface_from_arrays(PrimitiveType::TRIANGLES, &arrays);
    }
}
